package com.buli.blmall.admin.common.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.buli.blmall.admin.common.domain.SysLoginUser;
import com.buli.blmall.admin.common.request.PwdLogin;
import com.buli.blmall.admin.common.request.SmsLogin;
import com.buli.blmall.admin.consts.CacheKey;
import com.buli.blmall.admin.consts.Constants;
import com.buli.blmall.admin.core.horder.LoginUserHolder;
import com.buli.blmall.admin.core.listener.LoginEventData;
import com.buli.blmall.admin.enums.LoginType;
import com.buli.blmall.admin.utils.ServletUtil;
import com.buli.blmall.admin.core.cache.RedisClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

/**
 * 登录服务
 *
 * @author xiang.gao
 * @date 2024/12/18 14:59
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysLoginService {

    private final RedisClient redisClient;
    private final AuthenticationManager authenticationManager;

    /**
     * 密码登录
     * @param sysLogin
     * @return
     */
    public SysLoginUser pwdLogin(PwdLogin sysLogin) {
        Authentication passwordAuthenticationToken = new UsernamePasswordAuthenticationToken(sysLogin.getUsername(), sysLogin.getPassword());
        //交给SecurityManager进行认证,具体在 PasswordAuthProvider 中实现
        Authentication authentication = authenticationManager.authenticate(passwordAuthenticationToken);
        SysLoginUser sysLoginUser = (SysLoginUser) authentication.getDetails();
        // 记录登录日志
        recordSuccessLoginHistory(sysLoginUser.getLoginType(), sysLoginUser.getPrincipal(), sysLoginUser.getToken());
        log.info("登录成功,用户名:{},token:{}", sysLoginUser.getPrincipal(), sysLoginUser.getToken());
        return sysLoginUser;
    }

    /**
     * 短信登录
     * @param smsLogin
     * @return
     */
    public SysLoginUser smsLogin(SmsLogin smsLogin) {
        OneTimeTokenAuthenticationToken oneTimeTokenAuthenticationToken = new OneTimeTokenAuthenticationToken(smsLogin.getPhoneNumber(), smsLogin.getSmsCode());
        //交给SecurityManager进行认证,具体在 PhoneSmsAuthProvider 中实现
        Authentication authentication = authenticationManager.authenticate(oneTimeTokenAuthenticationToken);
        SysLoginUser sysLoginUser = (SysLoginUser) authentication.getDetails();
        // 记录登录日志
        recordSuccessLoginHistory(sysLoginUser.getLoginType(), sysLoginUser.getPrincipal(), sysLoginUser.getToken());
        log.info("登录成功,用户名:{},token:{}", sysLoginUser.getPrincipal(), sysLoginUser.getToken());
        return sysLoginUser;
    }

    /**
     * 退出登录
     *
     */
    public void logout() {
        SysLoginUser sysLoginUser = LoginUserHolder.get();
        if (sysLoginUser == null) {
            log.warn("登出失败,当前未登录.");
            return;
        }
        SecurityContextHolder.clearContext();
        // 删除缓存
        redisClient.deleteValue(CacheKey.SYSTEM_LOGIN_TOKEN.concat(sysLoginUser.getToken()));
        // 记录登录历史
        recordLoginHistory(sysLoginUser.getLoginType(), sysLoginUser.getPrincipal(), Constants.LOGOUT, Constants.LOGIN_SUCCESS);
    }

    /**
     * 记录登录成功
     * @param sysLogin
     * @param principal
     * @param token
     */
    protected void recordSuccessLoginHistory(LoginType sysLogin, String principal, String token) {
        LoginEventData eventData = new LoginEventData();
        eventData.setLoginType(sysLogin.name());
        eventData.setPrincipal(principal);
        eventData.setToken(token);
        eventData.setStatus(Constants.LOGIN_SUCCESS);
        eventData.setLoginIp(ServletUtil.getClientIP());
        eventData.setDeviceInfo(ServletUtil.getHeader("User-Agent", Constants.UTF8));
        eventData.setLoginTime(DateUtil.date());
        // 异步记录登录日志
        SpringUtil.getApplicationContext().publishEvent(eventData);
    }

    /**
     * 记录登录历史
     *
     * @param sysLogin
     * @param status
     * @param message
     */
    private void recordLoginHistory(LoginType sysLogin, String principal, String status, String message) {
        LoginEventData eventData = new LoginEventData();
        eventData.setLoginType(sysLogin.name());
        eventData.setPrincipal(principal);
        eventData.setStatus(status);
        eventData.setMessage(message);
        eventData.setLoginIp(ServletUtil.getClientIP());
        eventData.setDeviceInfo(ServletUtil.getHeader("User-Agent", Constants.UTF8));
        eventData.setLoginTime(DateUtil.date());
        // 异步记录登录日志
        SpringUtil.getApplicationContext().publishEvent(eventData);
    }

}
